﻿using System;

namespace Tiny.Imaging.Filter.ChannelBlendLibrary
{
    public struct ChannelBlend
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Normal(int baseColor, int blendColor)
        {
            return blendColor;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Lighten(int baseColor, int blendColor)
        {
            return baseColor < blendColor ? blendColor : baseColor;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Darken(int baseColor, int blendColor)
        {
            return baseColor < blendColor ? baseColor : blendColor;
        }

        /// <summary>
        /// Usage Reference:
        /// Equal to Lightness filter
        /// White Opacity [0 - 100] = Lightness [0 - 100]
        /// Black Opacity [100 - 0] = Lightness [-100 - 0]
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Multiply(int baseColor, int blendColor)
        {
            return MathLibrary.MathLibrary.Clamp((baseColor * blendColor) / 255.0f);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Average(int baseColor, int blendColor)
        {
            return MathLibrary.MathLibrary.Clamp((baseColor + blendColor) / 2.0f);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Add(int baseColor, int blendColor)
        {
            return MathLibrary.MathLibrary.Clamp(baseColor + blendColor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Substract(int baseColor, int blendColor)
        {
            return MathLibrary.MathLibrary.Clamp(baseColor + blendColor - 255);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Difference(int baseColor, int blendColor)
        {
            return Math.Abs(baseColor - blendColor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Negation(int baseColor, int blendColor)
        {
            return 255 - Math.Abs(255 - baseColor - blendColor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Screen(int baseColor, int blendColor)
        {
            return 255 - (((255 - baseColor) * (255 - blendColor)) >> 8);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Exclusion(int baseColor, int blendColor)
        {
            return MathLibrary.MathLibrary.Clamp(baseColor + blendColor - 2 * baseColor * blendColor / 255.0f);
        }

        /// <summary>
        /// Usage Reference:
        /// The result is equal to Brightness filter and Overlay Blend mode in Photoshop
        /// White Opacity [0 - 100] = Brightness [0 - 150]
        /// Black Opacity [58 - 0] = Brightness [-150 - 0]
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Overlay(int baseColor, int blendColor)
        {
            return
                MathLibrary.MathLibrary.Clamp((baseColor < 128)
                                      ? (2 * baseColor * blendColor / 255.0f)
                                      : (255.0f - 2 * (255.0f - baseColor) * (255.0f - blendColor) / 255.0f));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int SoftLight(int baseColor, int blendColor)
        {
            float baseValue = baseColor / 255.0f;
            float blendValue = blendColor / 255.0f;
            return
                MathLibrary.MathLibrary.Clamp(255 *
                                  (blendValue < 0.5f
                                       ? 2 * baseValue * blendValue + baseValue * baseValue * (1 - 2 * blendValue)
                                       : MathLibrary.MathLibrary.Sqrt(baseValue) * (2 * blendValue - 1) + 2 * baseValue * (1 - blendValue)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int HardLight(int baseColor, int blendColor)
        {
            return MathLibrary.MathLibrary.Clamp(Overlay(blendColor, baseColor));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int ColorDodge(int baseColor, int blendColor)
        {
            return
                MathLibrary.MathLibrary.Clamp((blendColor == 255)
                                      ? blendColor
                                      : Math.Min(255.0f, ((baseColor << 8) / (255.0f - blendColor))));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int ColorBurn(int baseColor, int blendColor)
        {
            return
                MathLibrary.MathLibrary.Clamp((blendColor == 0)
                                      ? blendColor
                                      : Math.Max(0, (255.0f - ((255 - baseColor) << 8) / blendColor)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int LinearDodge(int baseColor, int blendColor)
        {
            return Add(baseColor, blendColor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int LinearBurn(int baseColor, int blendColor)
        {
            return Substract(baseColor, blendColor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int LinearLight(int baseColor, int blendColor)
        {
            return (blendColor < 128)
                       ? LinearBurn(baseColor, (2 * blendColor))
                       : LinearDodge(baseColor, (2 * (blendColor - 128)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int VividLight(int baseColor, int blendColor)
        {
            return (blendColor < 128)
                       ? ColorBurn(baseColor, (2 * blendColor))
                       : ColorDodge(baseColor, (2 * (blendColor - 128)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int PinLight(int baseColor, int blendColor)
        {
            return (blendColor < 128) ? Darken(baseColor, (2 * blendColor)) : Lighten(baseColor, (2 * (blendColor - 128)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int HardMix(int baseColor, int blendColor)
        {
            return (VividLight(baseColor, blendColor) < 128) ? 0 : 255;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Reflect(int baseColor, int blendColor)
        {
            return
                MathLibrary.MathLibrary.Clamp((blendColor == 255)
                                      ? blendColor
                                      : Math.Min(255, (baseColor * baseColor / (255.0f - blendColor))));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Glow(int baseColor, int blendColor)
        {
            return Reflect(blendColor, baseColor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <returns></returns>
        public static int Phoenix(int baseColor, int blendColor)
        {
            return Math.Min(baseColor, blendColor) - Math.Max(baseColor, blendColor) + 255;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <param name="opacity"></param>
        /// <returns></returns>
        public static int Alpha(int baseColor, int blendColor, float opacity)
        {
            return MathLibrary.MathLibrary.Clamp(opacity * baseColor + (1 - opacity) * blendColor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseColor"></param>
        /// <param name="blendColor"></param>
        /// <param name="func"></param>
        /// <param name="opacity"></param>
        /// <returns></returns>
        public static int AlphaFunc(int baseColor, int blendColor, Func<int, int, int> func, int opacity)
        {
            return Alpha(func(baseColor, blendColor), baseColor, opacity / 100.0f);
        }
    }
}